package com.youth.routercore

import android.app.Activity
import android.content.Context
import android.content.Intent
import com.youth.option.LifeRouterPotion.Companion.clarityPotion
import com.youth.option.LifeRouterPotion.Companion.currentActivity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.Objects.*

class RouterPathDispatcher {
    companion object {
        const val RAW_SCHEME = "youth_router_scheme"

        private const val PATH_TYPE_NONE = 0
        private const val PATH_TYPE_ACTION = 1
        private const val PATH_TYPE_ACTIVITY = 2
        private const val PATH_TYPE_ResultAction = 3

    }

    fun dispatch(request: RouterPathRequest, needResult: Boolean): Flow<Result<Intent>> {
        if (request.className.isEmpty()) {
            "path:${request.scheme} --> class not found!".logw()
            return flowOf(Result.failure(IllegalStateException("PATH class not found!")))
        }

        val cls = Class.forName(request.className)
        return when (getPathType(cls)) {
            PATH_TYPE_ACTIVITY -> dispatchActivity(request, needResult)
            PATH_TYPE_ACTION -> dispatchAction(request)
            PATH_TYPE_ResultAction -> dispatchResultAction(request)
            else -> {
                "path:${request.scheme} --> type error".logw()
                flowOf(Result.failure(IllegalStateException("PATH type error")))
            }
        }
    }

    private fun dispatchResultAction(
        request: RouterPathRequest,
    ): Flow<Result<Intent>> {
        val context = getContext()
        if (context == null) {
            "path=${request.scheme} but context can not be null".logw()
            return emptyFlow()
        }
        val cls = Class.forName(request.className)
        val action = cls.newInstance() as RouterResultAction
        return action.doResultAction(context, request.scheme, request.bundle) ?: emptyFlow()

    }

    private fun dispatchActivity(
        request: RouterPathRequest,
        needResult: Boolean
    ): Flow<Result<Intent>> {

            val context = getContext()
            if (context == null) {
                "path=${request.scheme} but context can not be null".logw()
                return emptyFlow()
            }
            val intent = createIntent(context, request)
            context.startActivity(intent)
        return    emptyFlow()
//        } else {
//            val currentActivity = currentActivity()
//            return if (currentActivity != null && currentActivity is FragmentActivity) {
//                val intent = createIntent(currentActivity, request)
//                RouterFragment.showAsFlow(currentActivity.supportFragmentManager, intent)
//            } else {
//                "path--> activity not found".logw()
//                flowOf(Result.failure(IllegalStateException("Activity not found")))
//            }
//        }
    }

    private fun dispatchAction(
        request: RouterPathRequest
    ): Flow<Result<Intent>> {
        val context = getContext()
        if (context == null) {
            "path=${request.scheme} but context can not be null".logw()
            return emptyFlow()
        }
        val cls = Class.forName(request.className)
        val action = cls.newInstance() as Action
        action.doAction(context, request.scheme, request.bundle)
        return emptyFlow()
    }

    private fun getPathType(cls: Class<*>): Int {
        return when {
            Action::class.java.isAssignableFrom(cls) -> PATH_TYPE_ACTION
            RouterResultAction::class.java.isAssignableFrom(cls) -> PATH_TYPE_ResultAction
            Activity::class.java.isAssignableFrom(cls) -> PATH_TYPE_ACTIVITY
            else -> PATH_TYPE_NONE
        }
    }

    private fun createIntent(context: Context, request: RouterPathRequest): Intent {
        val intent = Intent()
        intent.putExtra(RAW_SCHEME, request.scheme)
        intent.setClassName(context.packageName, request.className)
        intent.putExtras(request.bundle)
        if (context !is Activity) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        return intent
    }

    private fun getContext(): Context? = currentActivity() ?: clarityPotion
}

class RouterServiceDispatcher {
    private val implObjMap = mutableMapOf<String, Any>()

    fun dispatch(request: RouterServiceRequest): Any? {
        val routerServiceClass = Class.forName(request.className)

        if (!check(request)) {
            "RouterService -> $request not found!".logw()
            return null //createProxyObj(routerServiceClass)
        }
        val implClass = Class.forName(request.implClassName)
        val implObj = implClass.getImplObj(request)

        // check extend
        if (routerServiceClass.isAssignableFrom(implClass)) {
            return implObj
        }

        return createProxyObj(routerServiceClass, implObj) { method, args ->
            if (args == null) {
                val findMethod = implClass.getDeclaredMethod(method.name)
                findMethod.invoke(implObj)
            } else {
                val findMethod = implClass.getDeclaredMethod(method.name, *method.parameterTypes)
                findMethod.invoke(implObj, *args)
            }
        }
    }

    private fun check(request: RouterServiceRequest): Boolean {
        if (request.implClassName.isEmpty()) {
            return false
        }
        return true
    }

    private fun Class<*>.getImplObj(request: RouterServiceRequest): Any {
        return if (request.isSingleton) {
            realGetImplObj(request)
        } else {
            newInstance()
        }
    }

    @Synchronized
    private fun Class<*>.realGetImplObj(request: RouterServiceRequest): Any {
        var find = implObjMap[request.implClassName]
        if (find == null) {
            val implObj = newInstance()
            implObjMap[request.implClassName] = implObj
            find = implObj
        }
        return find!!
    }

    private fun createProxyObj(
        cls: Class<*>,
        implObj: Any = Any(),
        block: (Method, Array<Any>?) -> Any? = { _, _ -> }
    ): Any? {
        return Proxy.newProxyInstance(
            Thread.currentThread().contextClassLoader,
            arrayOf(cls)
        ) { _, method, args ->
            try {
                if (method.name == "hashCode") {
                    hashCode(implObj)
                } else if (method.name == "toString") {
                    toString(implObj)
                } else if (method.name == "equals") {
                    val other = args[0]
                    if (hashCode(implObj) == other.hashCode()) {
                        true
                    } else {
                        equals(implObj, other)
                    }
                } else {
                    block(method, args)
                }
            } catch (e: Exception) {
                if (e is NoSuchMethodException) {
                    "RouterService -> Method ${e.message} not found!".logw()
                } else {
                    e.logw()
                }
                Unit
            }
        }
    }
}